// Copyright 2012 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef V8_V8THREADS_H_
#define V8_V8THREADS_H_

#include <atomic>

#include "src/isolate.h"

namespace v8 {
namespace internal {

    class RootVisitor;
    class ThreadLocalTop;

    class ThreadState {
    public:
        // Returns nullptr after the last one.
        ThreadState* Next();

        enum List { FREE_LIST,
            IN_USE_LIST };

        void LinkInto(List list);
        void Unlink();

        // Id of thread.
        void set_id(ThreadId id) { id_ = id; }
        ThreadId id() { return id_; }

        // Should the thread be terminated when it is restored?
        bool terminate_on_restore() { return terminate_on_restore_; }
        void set_terminate_on_restore(bool terminate_on_restore)
        {
            terminate_on_restore_ = terminate_on_restore;
        }

        // Get data area for archiving a thread.
        char* data() { return data_; }

    private:
        explicit ThreadState(ThreadManager* thread_manager);
        ~ThreadState();

        void AllocateSpace();

        ThreadId id_;
        bool terminate_on_restore_;
        char* data_;
        ThreadState* next_;
        ThreadState* previous_;

        ThreadManager* thread_manager_;

        friend class ThreadManager;
    };

    class ThreadVisitor {
    public:
        // ThreadLocalTop may be only available during this call.
        virtual void VisitThread(Isolate* isolate, ThreadLocalTop* top) = 0;

    protected:
        virtual ~ThreadVisitor() = default;
    };

    class ThreadManager {
    public:
        void Lock();
        void Unlock();

        void InitThread(const ExecutionAccess&);
        void ArchiveThread();
        bool RestoreThread();
        void FreeThreadResources();
        bool IsArchived();

        void Iterate(RootVisitor* v);
        void IterateArchivedThreads(ThreadVisitor* v);
        bool IsLockedByCurrentThread() const
        {
            return mutex_owner_.load(std::memory_order_relaxed) == ThreadId::Current();
        }

        ThreadId CurrentId();

        void TerminateExecution(ThreadId thread_id);

        // Iterate over in-use states.
        ThreadState* FirstThreadStateInUse();
        ThreadState* GetFreeThreadState();

    private:
        explicit ThreadManager(Isolate* isolate);
        ~ThreadManager();

        void DeleteThreadStateList(ThreadState* anchor);

        void EagerlyArchiveThread();

        base::Mutex mutex_;
        // {ThreadId} must be trivially copyable to be stored in {std::atomic}.
        ASSERT_TRIVIALLY_COPYABLE(i::ThreadId);
        std::atomic<ThreadId> mutex_owner_;
        ThreadId lazily_archived_thread_;
        ThreadState* lazily_archived_thread_state_;

        // In the following two lists there is always at least one object on the list.
        // The first object is a flying anchor that is only there to simplify linking
        // and unlinking.
        // Head of linked list of free states.
        ThreadState* free_anchor_;
        // Head of linked list of states in use.
        ThreadState* in_use_anchor_;

        Isolate* isolate_;

        friend class Isolate;
        friend class ThreadState;
    };

} // namespace internal
} // namespace v8

#endif // V8_V8THREADS_H_
